<template>
  <a-form ref="formRef" layout="vertical" :model="_value" :rules="rules">
    <template v-for="(item, index) in forms as any" :key="item.name + index">
      <a-form-item :field="item.id" :label="item.title" v-show="showItem(item)">
        <template #label>
          <span>{{ item.title }}</span>
          <div v-show="item.props.showDescription">
            <a-tooltip :content="item.props.description" position="top">
              <icon-question-circle-fill />
            </a-tooltip>
          </div>
        </template>
        <form-design-render :ref="`sub-item_${item.id}`" v-model="_value[item.id]" :mode="mode" :config="item" />
      </a-form-item>
    </template>
  </a-form>
</template>

<script setup lang="ts">
import { ref, computed, getCurrentInstance, nextTick, watch } from "vue";
import { cloneDeep, isNaN } from "lodash";
import FormDesignRender from "./FormDesignRender.vue";
import { CompareFuncs } from "./funcComponents/compare/CompareOptions.ts";

const { proxy } = getCurrentInstance();
const props = defineProps({
  forms: {
    type: Array,
    default: () => []
  },
  mode: {
    type: String,
    default: "DESIGN"
  },
  config: {
    type: Object,
    default: () => {
      return {
        ruleType: "SIMPLE",
        rules: []
      };
    }
  },
  modelValue: {
    type: Object,
    default: () => ({})
  }
});
const emit = defineEmits(["update:modelValue"]);

const formPermHis = ref([]);

const _value = computed({
  get: () => {
    return props.modelValue;
  },
  set: val => {
    emit("update:modelValue", val);
  }
});
const _forms = computed(() => {
  return cloneDeep(props.forms);
});
const rules = computed(() => {
  return loadFormConfig(_forms.value);
});

/**
 * 校验
 */
const validate = async () => {
  await nextTick();
  return await proxy.$refs.formRef?.validate();
};
const initDefaultValue = (formItem: any) => {
  let value: any = "";
  ["TextInput", "TextareaInput", "DateTime", "SelectInput", "HyperlinkInput"].includes(formItem.name) && (value = "");
  formItem.name === "EditorInput" && (value = `<p>${value}</p>`);
  formItem.name === "VoiceToText" && (value = { txt: "", value: "" });
  ["NumberInput"].includes(formItem.name) && (value = 0);
  ["MultipleSelect", "CascaderInput", "ImageUpload", "FileUpload", "UserPicker"].includes(formItem.name) && (value = []);
  return value;
};

/**
 * 判断是否显示表单项
 * @param item
 * @return {boolean}
 */
const showItem = item => {
  return item.perm !== "H";
};

/**
 * 表单配置
 * @param forms
 */
const loadFormConfig = forms => {
  let rules = {};
  forms.forEach(item => {
    const value = props.modelValue[item.id] || initDefaultValue(item);
    _value.value[item.id] = item.name === "NumberInput" && !isNaN(parseFloat(value)) ? parseFloat(value) : value;
    if (item.props.required && showItem(item)) {
      rules[item.id] = [
        {
          type: getValidType(item),
          required: true,
          message: item.props.validateFailMessage || `请填写${item.title}`
        }
      ];
    }
  });
  return rules;
};
const getValidType = item => {
  switch (item.valueType) {
    case "Array":
    case "User":
    case "Dept":
      return "array";
    case "String":
    case "Date":
      return "string";
    case "Number":
      return "number";
    default:
      return item.valueType;
  }
};

/**
 * 设置显示表单字段
 * @param id
 */
const showField = id => {
  const field = formItemMap.value.get(id);
  field && (field.perm = formPermHis.value[id] || "E");
};
/**
 * 隐藏表单字段
 * @param id
 */
const hideField = id => {
  const field = formItemMap.value.get(id);
  field && (field.perm = "H");
};

/**
 * 比较
 * @param condition
 * @return {*}
 */
const compare = condition => {
  //取条件组件的值
  const source = _value.value[condition.field];
  //动态调用函数
  let compareType = null;
  switch (condition.fieldType) {
    case "SelectInput":
      compareType = "strCompare";
      break;
    case "MultipleSelect":
      compareType = "strArrCompare";
      break;
  }
  return CompareFuncs[compareType][condition.compare](
    source,
    condition.fixed ? condition.compareVal : _value.value[condition.compareVal[0]]
  );
};

/**
 * 解析表单联动规则
 */
const parserRule = cdRule => {
  const condition = cdRule.condition;
  try {
    return compare(condition);
  } catch (e) {
    return false;
  }
};

/**
 * 触发相应操作
 */
const doActions = async actions => {
  actions?.forEach(action => {
    switch (action.type) {
      case "SHOW":
        action.targets.forEach(tg => showField(tg));
        break;
      case "HIDE":
        action.targets.forEach(tg => hideField(tg));
        break;
    }
  });
};
const analyseRules = () => {
  props.config?.rules?.forEach(rule => {
    doActions(parserRule(rule.condition) ? rule.action.with : rule.action.other);
  });
};

/**
 * 缓存表单字段
 * @param forms
 * @param map
 */
const loadFormItemMap = (forms, map) => {
  forms.forEach(item => {
    map.set(item.id, item);
  });
};
const formItemMap = computed(() => {
  const map = new Map();
  loadFormItemMap(props.forms, map);
  return map;
});
/**
 * 监听值变化，触发表单联动规则
 */
watch(
  () => _value.value,
  () => {
    if (props.config?.rules?.length) {
      if (!Object.keys(formPermHis.value)?.length) {
        formItemMap.value.forEach(item => {
          formPermHis.value[item.id] = item.perm;
        });
      }
      analyseRules();
    }
  },
  { deep: true, immediate: true }
);

/**
 * 提交表单
 */
const submitForm = async () => {
  return await validate();
};
/**
 * 重置表单
 * @param callBack
 * @return {Promise<void>}
 */
const resetForm = async callBack => {
  await nextTick();
  proxy.$refs.formRef.resetFields();
  callBack && callBack();
};

defineExpose({
  submitForm,
  resetForm
});
</script>

<style lang="less" scoped>
.process-form {
  :deep(.el-form-item) {
    margin-bottom: 15px;
  }
  :deep(.el-form-item__label) {
    padding: 0 0;
  }
}
</style>
